package tk.redbluelotto.webapp.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import tk.redbluelotto.cache.QueryCache;
import tk.redbluelotto.cache.RedBallCache;
import tk.redbluelotto.model.RedBall;
import tk.redbluelotto.service.AndRule;
import tk.redbluelotto.service.ArithmeticRule;
import tk.redbluelotto.service.IRule;
import tk.redbluelotto.service.LastValueRule;
import tk.redbluelotto.util.CalculateUtil;
import tk.redbluelotto.view.RedBallForm;
import tk.redbluelotto.view.RedNum;

@Controller
@RequestMapping("/calculate")
public class CalculateController {

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView handleRequest() throws Exception {
		return new ModelAndView("/calculate", "redForm", initRedBallForm(new RedBallForm()));
	}

	@RequestMapping(method = RequestMethod.POST)
	public ModelAndView calculate(@ModelAttribute("redForm") RedBallForm form) {
		long start = System.currentTimeMillis();
		List<RedNum> redballs = null;
		Map<Long, RedBall> resultMap;
		if (form.getMod3() != null) {
			long fromKey = ((long) (form.getMod3() / 10)) << 57;
			long toKey;
			if (form.getMod2() != null) {
				fromKey |= ((long) form.getMod2()) << 54;
				if (form.getPrime() != null) {
					fromKey |= ((long) form.getPrime()) << 51;
					toKey = fromKey | 0x7FFFFFFFFFFFFL;
				} else {
					toKey = fromKey | 0X3FFFFFFFFFFFFFL;
				}
			} else {
				toKey = fromKey | 0X1FFFFFFFFFFFFFFL;
			}
			resultMap = RedBallCache.getInstance().getRedBallTreeMap().subMap(fromKey, toKey);
		} else {
			resultMap = RedBallCache.getInstance().getRedBallTreeMap();
		}
		redballs = _calculate(form, resultMap);
		ModelAndView mv = new ModelAndView("/calculate", "redballs", redballs);
		form.setTime(System.currentTimeMillis() - start);
		mv.addObject("redForm", initRedBallForm(form));
		return mv;
	}

	private List<RedNum> _calculate(RedBallForm form, Map<Long, RedBall> redballs) {
		List<IRule> rules = new ArrayList<IRule>();
		List<RedNum> result = new ArrayList<RedNum>();
		if (form.getAndRule() != null && !form.getAndRule().trim().equals("")) {
			rules.add(new AndRule(form.getAndRule()));
		}
		if ((form.getLogicRule() != null && !form.getLogicRule().trim().equals("")) || !form.getAdjacent().trim().equals("")) {
			ArithmeticRule arithmeticRule = new ArithmeticRule();
			if (form.getAdjacent() != null)
				arithmeticRule.addPreQueryRule(QueryCache.getInstance().getAdjacent(form.getAdjacent()));
			if (form.getLogicRule() != null && !form.getLogicRule().trim().equals(""))
				arithmeticRule.init(form.getLogicRule());
			rules.add(arithmeticRule);
		}
		if ((form.getDupRule() != null && !form.getDupRule().trim().equals("")) || form.getDuplication() != null) {
			String ruleString = form.getDuplication() == null ? "" : ("LAST_1=" + form.getDuplication() + ":" + (6 - form.getDuplication()));
			rules.add(new LastValueRule(ruleString + (form.getDupRule() == null || form.getDupRule().trim().equals("") ? "" : ("," + form.getDupRule().trim()))));
		}
		boolean satisfied;
		RedBall redball;
		for (Map.Entry<Long, RedBall> entry : redballs.entrySet()) {
			satisfied = true;
			redball = entry.getValue();
			for (IRule rule : rules) {
				if (!rule.isSatisfied(redball)) {
					satisfied = false;
					break;
				}
			}
			if (!satisfied)
				continue;
			result.add(CalculateUtil.transToView(redball));
		}
		return result;
	}

	private RedBallForm initRedBallForm(RedBallForm redballForm) {
		Map<String, String> adjacentList = new LinkedHashMap<String, String>();
		adjacentList.put("000000", "------");
		adjacentList.put("120000", "❶❷----");
		adjacentList.put("023000", "-❷❸---");
		adjacentList.put("003400", "--❸❹--");
		adjacentList.put("000450", "---❹❺-");
		adjacentList.put("000056", "----❺❻");
		adjacentList.put("123000", "❶❷❸---");
		adjacentList.put("023400", "-❷❸❹--");
		adjacentList.put("003450", "--❸❹❺-");
		adjacentList.put("000456", "---❹❺❻");
		adjacentList.put("120450", "❶❷-❹❺-");
		adjacentList.put("120056", "❶❷--❺❻");
		adjacentList.put("023056", "-❷❸-❺❻");
		adjacentList.put("123400", "❶❷❸❹--");
		adjacentList.put("023450", "-❷❸❹❺-");
		adjacentList.put("003456", "--❸❹❺❻");
		adjacentList.put("120456", "❶❷-❹❺❻");
		adjacentList.put("123056", "❶❷❸-❺❻");
		adjacentList.put("123450", "❶❷❸❹❺-");
		adjacentList.put("023456", "-❷❸❹❺❻");
		adjacentList.put("123456", "❶❷❸❹❺❻");
		redballForm.setAdjacentList(adjacentList);
		Map<String, String> num = new HashMap<String, String>();
		num.put("0", "0");
		num.put("1", "1");
		num.put("2", "2");
		num.put("3", "3");
		num.put("4", "4");
		num.put("5", "5");
		num.put("6", "6");
		redballForm.setMod2List(num);
		redballForm.setDuplicationList(num);
		redballForm.setPrimeList(num);
		Map<String, String> mod3List = new LinkedHashMap<String, String>();
		mod3List.put("600", "6:0:0");
		mod3List.put("510", "5:1:0");
		mod3List.put("501", "5:0:1");
		mod3List.put("420", "4:2:0");
		mod3List.put("411", "4:1:1");
		mod3List.put("330", "3:3:0");
		mod3List.put("321", "3:2:1");
		mod3List.put("312", "3:1:2");
		mod3List.put("303", "3:0:3");
		mod3List.put("240", "2:4:0");
		mod3List.put("231", "2:3:1");
		mod3List.put("222", "2:2:2");
		mod3List.put("213", "2:1:3");
		mod3List.put("204", "2:0:4");
		mod3List.put("150", "1:5:0");
		mod3List.put("141", "1:4:1");
		mod3List.put("132", "1:3:2");
		mod3List.put("123", "1:2:3");
		mod3List.put("114", "1:1:4");
		mod3List.put("105", "1:0:5");
		mod3List.put("060", "0:6:0");
		mod3List.put("051", "0:5:1");
		mod3List.put("042", "0:4:2");
		mod3List.put("033", "0:3:3");
		mod3List.put("024", "0:2:4");
		mod3List.put("015", "0:1:5");
		mod3List.put("006", "0:0:6");
		redballForm.setMod3List(mod3List);
		return redballForm;
	}

}
